using System;
using System.Collections.Generic;
using System.Text;

namespace Common.Collections
{
    /// <summary>
    /// A Sorted List that can contain multiple values per key.
    /// </summary>
    /// <typeparam name="TKey">The Key Type</typeparam>
    /// <typeparam name="TValue">The Value Type</typeparam>
    /// <remarks>The values are returned in order according to their key, when multiple values have the same key, they are returned in the order they were added.</remarks>
    public class SortedMultiList<TKey, TValue> : IDictionary<TKey, TValue>
    {
        private OrderedList<TKey, List<TValue>> collection;

        /// <summary>
        /// The inner collection used by this SortedMultiList
        /// </summary>
        protected OrderedList<TKey, List<TValue>> Collection
        {
            get
            {
                return this.collection;
            }
        }

        public SortedMultiList()
        {
            this.collection = new OrderedList<TKey, List<TValue>>();
        }

        public SortedMultiList(IComparer<TKey> comparer)
        {
            this.collection = new OrderedList<TKey, List<TValue>>(comparer);
        }

        public SortedMultiList(IDictionary<TKey, TValue> dictionary)
        {
            this.collection = new OrderedList<TKey, List<TValue>>();

            foreach (KeyValuePair<TKey, TValue> pair in dictionary)
            {
                this.Add(pair);
            }
        }

        public SortedMultiList(IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer)
        {
            this.collection = new OrderedList<TKey, List<TValue>>(comparer);

            foreach (KeyValuePair<TKey, TValue> pair in dictionary)
            {
                this.Add(pair);
            }
        }

        public SortedMultiList(Int32 capacity)
        {
            this.collection = new OrderedList<TKey, List<TValue>>(capacity);
        }

        public SortedMultiList(Int32 capacity, IComparer<TKey> comparer)
        {
            this.collection = new OrderedList<TKey, List<TValue>>(capacity, comparer);
        }

        /// <summary>
        /// Add an Element to the List.
        /// </summary>
        /// <param name="key">The key for the element.</param>
        /// <param name="value">The value for the element</param>
        public void Add(TKey key, TValue value)
        {
            if (!this.collection.ContainsKey(key)) this.collection.Add(key, new List<TValue>());

            this.collection[key].Add(value);
        }

        public bool ContainsKey(TKey key)
        {
            return this.collection.ContainsKey(key);
        }

        public ICollection<TKey> Keys
        {
            get
            {
                return this.collection.Keys;
            }
        }

        public bool Remove(TKey key)
        {
            return this.collection.Remove(key);
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            value = default(TValue);

            if (!this.collection.ContainsKey(key)) return false;

            if (this.collection[key].Count == 0) return false;

            value = this.collection[key][0];

            return true;
        }

        public ICollection<TValue> Values
        {
            get
            {
                List<TValue> values = new List<TValue>();

                foreach (KeyValuePair<TKey, List<TValue>> pair in this.collection)
                {
                    foreach (TValue value in pair.Value)
                    {
                        values.Add(value);
                    }
                }

                return values;
            }
        }

        public TValue this[TKey key]
        {
            get
            {
                if (!this.collection.ContainsKey(key)) return default(TValue);
                if (this.collection[key].Count == 0) return default(TValue);
                return this.collection[key][0];
            }
            set
            {
                this.Add(key, value);
            }
        }

        #region ICollection<KeyValuePair<TKey,TValue>> Members

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            this.Add(item.Key, item.Value);
        }

        public void Clear()
        {
            this.collection.Clear();
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            if (!this.collection.ContainsKey(item.Key)) return false;

            return this.collection[item.Key].Contains(item.Value);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            foreach (KeyValuePair<TKey, List<TValue>> pair in this.collection)
            {
                foreach(TValue value in pair.Value)
                {
                    array[arrayIndex++] = new KeyValuePair<TKey,TValue>(pair.Key, value);
                }
            }
        }

        public int Count
        {
            get
            {
                int count = 0;
                foreach (KeyValuePair<TKey, List<TValue>> pair in this.collection)
                {
                    count += pair.Value.Count;
                }
                return count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            if (!this.collection.ContainsKey(item.Key)) return false;

            return this.collection[item.Key].Remove(item.Value);
        }

        #endregion

        #region IEnumerable<KeyValuePair<TKey,TValue>> Members

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            foreach (KeyValuePair<TKey, List<TValue>> pair in this.collection)
            {
                foreach (TValue value in pair.Value)
                {
                    yield return new KeyValuePair<TKey, TValue>(pair.Key, value);
                }
            }
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (System.Collections.IEnumerator)this.GetEnumerator();
        }

        #endregion
    }
}
